﻿using System.Collections.Generic;
using SpreadsheetGear;
using SpreadsheetGear.Windows.Controls;
using System;
using System.Linq;
using WPFWorkbookDesigner.Addons;

namespace WPFWorkbookDesigner.Model.SpreadSheet
{
    public class FindModel
    {
        public FindModel(IRange rangeSelection)
        {
            RangeSelection = rangeSelection;
        }

        #region main properties

        public Within Within { get; set; }
        public IRange RangeSelection { get; private set; }
        public IRange AfterRange { get; set; }
        public string FindWhat { get; set; }
        public LookAt LookAt { get; set; }
        public FindLookIn LookIn { get; set; }
        public SearchDirection SearchDirection { get; set; }
        public SearchOrder SearchOrder { get; set; }
        public bool MatchCase { get; set; }

        #endregion


        #region Add range

        private bool AddAllRange(List<IRange> list, IWorkbooks books)
        {
            switch (Within)
            {
                case Within.WorkbookSet:
                    break;
                case Within.Workbook:
                    break;
                default:
                    break;

            }
            return true;
        }
        private bool AddRange(List<IRange> list, IRange range, bool checkLocked)
        {
            if ((checkLocked && range.Worksheet.ProtectContents) && (range.Locked || !range.LockedDefined))
                return false;

            list.Add(range);
            return true;
        }
        private bool AddRangesOfWorkbook(List<IRange> list, IWorkbook workbook, bool checkLocked)
        {
            IWorksheets worksheets = workbook.Worksheets;
            if (SearchDirection == SearchDirection.Next)
            {
                for (int i = 0 ; i < worksheets.Count ; i++)
                {
                    IWorksheet worksheet = worksheets[i];
                    if ((worksheet.Visible == SheetVisibility.Visible) && !AddRange(list, worksheet.Cells, checkLocked))
                        return false;
                }
            }
            else
            {
                for (int i = worksheets.Count - 1 ; i > 0 ; i--)
                {
                    IWorksheet worksheet = worksheets[i];
                    if ((worksheet.Visible == SheetVisibility.Visible) && !AddRange(list, worksheet.Cells, checkLocked))
                        return false;
                }
            }
            return true;
        }
        private bool AddRangesOfWorkbooks(List<IRange> list, IWorkbooks workbooks, bool checkLocked)
        {
            if (SearchDirection == SearchDirection.Next)
            {
                for (int i = 0 ; i < workbooks.Count ; i++)
                {
                    if (!AddRangesOfWorkbook(list, workbooks[i], checkLocked))
                        return false;
                }
            }
            else
            {
                for (int i = workbooks.Count - 1 ; i > 0 ; i--)
                {
                    if (!AddRangesOfWorkbook(list, workbooks[i], checkLocked))
                        return false;
                }
            }

            return true;
        }
        private IRange GetAfterRange(IRange range)
        {
            return SearchDirection == SearchDirection.Next ?
                range.Range[range.RowCount - 1, range.ColumnCount - 1]
                : range.Range[0, 0];
        }
        #endregion

        public IRange FindNext()
        {
            IRange activeCell = AfterRange;
            return activeCell.WorkbookSet.Dowork(s =>
            {
                IRange found;
                switch (Within)
                {
                    default:
                        found = FindNext(RangeSelection, activeCell);
                        if (!IsResultAvailable(found, activeCell))
                            found = null;
                        break;
                    case Within.Worksheet:
                        found = FindNextWithinWorksheet(RangeSelection.Worksheet, activeCell);
                        break;
                    case Within.Workbook:
                        found = FindNextWithinWorkbook(RangeSelection.Worksheet.Workbook, activeCell);
                        break;
                    case Within.WorkbookSet:
                        found = FindNextWithinWorkbooks(RangeSelection.Worksheet.Workbook, activeCell);
                        break;
                }
                return found;
            });
        }

        private IRange FindNext(IRange search, IRange after)
        {
            if (after == null)
                after = GetAfterRange(search);
            IRange found = search.Find(FindWhat, after, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase);
            return found;
        }

        private IRange FindNextWithinWorksheet(IWorksheet sheet, IRange activeCell)
        {
            var found = FindNext(sheet.Range, activeCell);
            return found;
        }
        private IRange FindNextWithinWorkbook(IWorkbook book, IRange activeCell)
        {
            IWorksheet activeWorksheet = activeCell.Worksheet;
            var found = FindNextWithinWorksheet(activeWorksheet, activeCell);
            if (IsResultAvailable(found, activeCell))
                return found;
            var worksheets = book.Worksheets;
            int index = activeWorksheet.Index;
            IWorksheet afterSheet;
            do
            {
                if (SearchDirection == SearchDirection.Next)
                {
                    index = index + 1 >= worksheets.Count ? 0 : index + 1;
                }
                else
                {
                    index = index - 1 < 0 ? worksheets.Count - 1 : index - 1;
                }
                afterSheet = worksheets[index];
                //afterRange = GetAfterRange(afterSheet.Range);
                found = FindNext(afterSheet.Range, null);
                if (found != null)
                    break;
            }
            while (!ReferenceEquals(afterSheet, activeWorksheet));
            return found;
        }

        private IRange FindNextWithinWorkbooks(IWorkbook activeBook, IRange activeCell)
        {
            IRange found;
            var activeSheet = activeCell.Worksheet;
            found = FindNextWithinWorkbook(activeBook, activeCell);
            if (found != null && IsResultAvailiable(activeSheet, found.Worksheet))
                return found;

            var workbooks = activeBook.WorkbookSet.Workbooks.OfType<IWorkbook>().ToList();
            var index = workbooks.IndexOf(activeBook);

            IWorkbook afterBook;
            do
            {
                int sheetIndex;
                if (SearchDirection == SearchDirection.Next)
                {
                    index = index + 1 >= workbooks.Count ? 0 : index + 1;
                    afterBook = workbooks[index];
                    sheetIndex = 0;
                }
                else
                {
                    index = index - 1 < 0 ? workbooks.Count - 1 : index - 1;
                    afterBook = workbooks[index];
                    sheetIndex = afterBook.Worksheets.Count - 1;
                }
                //afterBook = workbooks[index];
                found = FindNextWithinWorksheet(afterBook.Worksheets[sheetIndex], null);
                if (found != null)
                    break;
            }
            while (!ReferenceEquals(activeBook, afterBook));
            return found;

        }
        #region find next

        private bool IsResultAvailable(IRange found, IRange active)
        {
            if (found == null || active == null)
                return false;
            switch (SearchDirection)
            {
                default:
                case SearchDirection.Next:
                    switch (SearchOrder)
                    {
                        default:
                        case SearchOrder.ByRows:
                            return found.Row >= active.Row;
                        case SearchOrder.ByColumns:
                            return found.Column >= active.Column;
                    }
                case SearchDirection.Previous:
                    switch (SearchOrder)
                    {
                        default:
                        case SearchOrder.ByRows:
                            return found.Row <= active.Row;
                        case SearchOrder.ByColumns:
                            return found.Column <= active.Column;
                    }
            }
        }

        private bool IsResultAvailiable(IWorksheet begin, IWorksheet found)
        {
            switch (SearchDirection)
            {
                default:
                case SearchDirection.Next:
                    return found.Index > begin.Index;
                case SearchDirection.Previous:
                    return found.Index < begin.Index;
            }
        }

        private bool IsResultAvailable(IList<IWorkbook> workbooks, IWorkbook start, IWorkbook found)
        {
            if (!workbooks.Contains(start) || !workbooks.Contains(found))
                return false;
            switch (SearchDirection)
            {
                default:
                case SearchDirection.Next:
                    return workbooks.IndexOf(found) > workbooks.IndexOf(start);
                case SearchDirection.Previous:
                    return workbooks.IndexOf(found) < workbooks.IndexOf(start);
            }
        }
        #endregion


        private void FindAll()
        {
            List<IRange> ranges = new List<IRange>();
            switch (Within)
            {
                default:
                    AddRange(ranges, RangeSelection, true);
                    break;
                case Within.Worksheet:
                    AddRange(ranges, RangeSelection.Worksheet.Cells, true);
                    break;
                case Within.Workbook:
                    AddRangesOfWorkbook(ranges, RangeSelection.Worksheet.Workbook, true);
                    break;
                case Within.WorkbookSet:
                    AddRangesOfWorkbooks(ranges, RangeSelection.WorkbookSet.Workbooks, true);
                    break;
            }

        }
        private void Replace()
        {
            RangeSelection.Replace(FindWhat, "RpWhat", LookAt, SearchOrder, MatchCase);
        }
    }
}